package com.solveit.aps.io.db;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import com.solveit.core.db2.generator.Manager;
import com.solveit.core.db2.generator.MetaClass;
import com.solveit.core.db2.generator.ObjectGenerator;
import com.solveit.core.db2.generator.PrimaryKey;
import com.solveit.core.db2.query.DbEntityHelper;
import com.solveit.core.db2.query.builder.And;
import com.solveit.core.db2.query.builder.C;
import com.solveit.core.db2.query.builder.Condition;
import com.solveit.core.db2.query.builder.Query;
import com.solveit.core.slog.SLogger;

/**
 * A shorthand class that creates simple queries.
 * 
 * @author sschellenberg
 * 
 */
public final class DBUtil {

//	protected DbEntityHelper helper;
//
//	public DBUtil(DbEntityHelper helper) {
//		this.helper = helper;
//	}

	/**
	 * Generates an update query and executes it.
	 * 
	 * @param obj
	 *            The object to be updates.
	 * @param aliases
	 *            The columns specified by this alias to be updates. If this is
	 *            not provided, all columns will be updated.
	 */
	public static Query.Update update(DbEntityHelper helper, Object obj, int... aliases) {
		if (PrimaryKey.create(obj).isNull()) {
			return null;
		}
		Query.Update upd = new Query.Update(obj.getClass());
		MetaClass meta = Manager.getMeta(obj.getClass());

		if (aliases == null || aliases.length == 0) {
			List<Field> updateFields = new ArrayList<Field>(meta.getProperties());
			updateFields.removeAll(meta.getPKFields());
			aliases = new int[updateFields.size()];
			for (int i = 0; i < aliases.length; i++) {
				aliases[i] = meta.getAliasByProperty(updateFields.get(i));
			}
		}

		Condition cond = null;
		if (meta.getPKAliases().length == 1) {
			Field field = meta.getPropertyByAlias(meta.getPKAliases()[0]);
			cond = C.eq(meta.getPKAliases()[0], ObjectGenerator.getPropertyValue(field, obj));
		} else {
			cond = C.and();
			for (int pkAlias : meta.getPKAliases()) {
				Field field = meta.getPropertyByAlias(pkAlias);
				((And) cond).add(C.eq(pkAlias, ObjectGenerator.getPropertyValue(field, obj)));
			}
		}
		upd.setCondition(cond);
		upd.include(aliases);
		SLogger.info("Updating " + meta.getPropertyByAlias(aliases[0]).getName() + (aliases.length > 1 ? "..." : "") + " of object " + obj);
		helper.exec(upd, obj);
		return upd;
	}

	/**
	 * Generates an insert query and executes it (non-transactional). The method
	 * will return in case not all mandatory fields (non-nullable) have been
	 * assigned or if the entity reference is null.
	 * 
	 * @param obj
	 *            The entity to be inserted.
	 */
	public static Query.Insert insert(DbEntityHelper helper, Object obj) {
		MetaClass meta = Manager.getMeta(obj.getClass());
		for (Field f : meta.getProperties()) {
			Object v = ObjectGenerator.getPropertyValue(f, obj);
			if (v == null && !(meta.isNullable(f) || meta.isAutoDate(f) || meta.isAutoId(f))) {
				throw new IllegalArgumentException("Field \""+f.getName()+"\" in class \""+obj.getClass().getName()+"\" cannot be null");
			}
		}
		SLogger.info("Inserting object " + obj);
		Query.Insert ins = new Query.Insert(meta.getClazz());
		helper.exec(ins, obj);
		return ins;
	}

	/**
	 * Generates a delete query and executes it (non-transactional).
	 * 
	 * @param obj
	 *            The entity to be deleted.
	 */
	public static void delete(DbEntityHelper helper, Object obj) {
		MetaClass meta = Manager.getMeta(obj.getClass());
		And condition = C.and();
		for (int i = 0; i < meta.getPKAliases().length; i++) {
			int alias = meta.getPKAliases()[i];
			Field field = meta.getPropertyByAlias(alias);
			Object value = ObjectGenerator.getPropertyValue(field, obj);
			condition.add(C.eq(alias, value));
		}
		if (condition.getTerms().length == 0) {
			// prevent wiping out all records
			return;
		}
		Query.Delete del = new Query.Delete(obj.getClass());
		del.setCondition(condition);
		SLogger.info("Deleting object " + obj);
		helper.exec(del);
	}

	public static Condition getCondition(Object obj) {
		MetaClass meta = Manager.getMeta(obj.getClass());
		And condition = C.and();
		for (int i = 0; i < meta.getPKAliases().length; i++) {
			int alias = meta.getPKAliases()[i];
			Field field = meta.getPropertyByAlias(alias);
			Object value = ObjectGenerator.getPropertyValue(field, obj);
			condition.add(C.eq(alias, value));
		}
		if (condition.getTerms().length == 0) {
			// prevent wiping out all records
			return null;
		}
		return condition;
	}

	public static Object getPropertyByAlias(Object entity, Integer alias) {
		MetaClass metaClass = MetaClass.create(entity.getClass());
		Field property = metaClass.getPropertyByAlias(alias);
		return ObjectGenerator.getPropertyValue(property, entity);
	}

	public static void setPropertyByAlias(Object entity, Integer alias, Object value) {
		MetaClass metaClass = Manager.getMeta(entity.getClass());
		Field property = metaClass.getPropertyByAlias(alias);
		ObjectGenerator.setPropertyValue(property, entity, value);
	}
	
	public static void resetId(Object entity) {
		if (entity != null) {
			MetaClass meta = Manager.getMeta(entity.getClass());
			for (Field pk : meta.getPKFields()) {
				if (meta.isAutoId(pk)) {
					ObjectGenerator.setPropertyValue(pk, entity, null);
				}
			}
		}
	}
}
